﻿using System.Runtime.Serialization;
[assembly: ContractNamespace("", ClrNamespace = "AgileZen.Models")]

namespace AgileZen
{
    using System;
    using System.Configuration;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using Microsoft.Runtime.Serialization.Json;
    using AgileZen.Models;

    /// <summary>
    /// 
    /// </summary>
    public class AgileZenService : IAgileZenService
    {
        #region Me

        public Person GetMe() 
        {
            return Get<Person>("me");
        }

        public Person GetMe(params string[] enrichments) 
        {
            string param = String.Empty;
            if (enrichments != null)
            {
                param = String.Format("?with={0}", String.Join(",", enrichments));
            }

            return Get<Person>(String.Format("me{0}", param));
        }

        [Obsolete]
        public void UpdateMe(ref Person me) 
        {
            me = Put<Person>("me", me);
        }

        public Stories GetMyStories() 
        {
            return Get<Stories>("me/stories");
        }

        #endregion

        #region Projects
        /// <summary>
        /// Gets the projects.
        /// </summary>
        /// <returns></returns>
        public Projects GetProjects()
        {
            return GetProjects(null);
        }

        /// <summary>
        /// Gets the projects.
        /// </summary>
        /// <param name="enrichments">The enrichments.</param>
        /// <returns></returns>
        public Projects GetProjects(params string[] enrichments)
        {
            return Get<Projects>("projects");
        }

        /// <summary>
        /// Gets the project.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        public Project GetProject(int projectid)
        {
            return GetProject(projectid, null);
        }

        /// <summary>
        /// Gets the project.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <param name="enrichments">The enrichments.</param>
        /// <returns></returns>
        public Project GetProject(int projectid, params string[] enrichments)
        {
            string param = String.Empty;
            if (enrichments != null)
            { 
                param = String.Format("?with={0}",String.Join(",",enrichments));
            }

            return Get<Project>(String.Format("projects/{0}{1}", projectid, param));
        }

        public People GetProjectMembers(int projectid) 
        {
            return Get<People>(String.Format("projects/{0}/members", projectid));
        
        }

        public People AddProjectMember(int projectId, int[] members)
        {
            return Post<int[],People>(String.Format("projects/{0}/members", projectId), members);
        }

        public People SetProjectMembers(int projectId, int[] members)
        {
            return Put<int[],People>(String.Format("projects/{0}/members", projectId), members);
        }

        public void RemoveProjectMember(int projectId, int userId)
        {
            Delete(String.Format("projects/{0}/members/{1}", projectId, userId));
        }


        public void UpdateProject(int projectid, ref Project project)
        {
            Put<Project>(String.Format("projects/{0}", project.Id),project);
        }

        #endregion

        #region Stories

        public void CreateStory(int projectId, ref Story story)
        {
            story = Post<Story>(String.Format("projects/{0}/stories", projectId), story);
        }

        /// <summary>
        /// Gets the stories.
        /// </summary>
        /// <param name="projectId">The project id.</param>
        /// <returns></returns>
        public Stories GetStories(int projectId)
        {
            return GetStories(projectId, null);
        }


        /// <summary>
        /// Gets the stories.
        /// </summary>
        /// <param name="projectId">The project id.</param>
        /// <returns></returns>
        public Stories GetStories(int projectId, int tagId)
        {
            return GetStories(projectId, tagId,null);
        }

        /// <summary>
        /// Gets the stories.
        /// </summary>
        /// <param name="projectId">The project id.</param>
        /// <param name="enrichments">The enrichments.</param>
        /// <returns></returns>
        public Stories GetStories(int projectId, params string[] enrichments)
        {
            string param = String.Empty;
            if (enrichments != null)
            {
                param = String.Format("?with={0}", String.Join(",", enrichments));
            }

            return Get<Stories>(String.Format("projects/{0}/stories{1}", projectId, param));
        }

        public Stories GetStories(int projectId, int tagId, params string[] enrichments) 
        {
            string param = String.Empty;
            if (enrichments != null)
            {
                param = String.Format("?with={0}", String.Join(",", enrichments));
            }

            return Get<Stories>(String.Format("projects/{0}/tags/{1}/stories{2}", projectId, tagId ,param));
        }

        public Stories SetStoriesForTag(int projectId, int tagId, int[] stories)
        {
            return Put<int[],Stories>(String.Format("projects/{0}/tags/{1}/stories", projectId, tagId), stories);
        }

        public Story SetTagsForStory(int projectId, int storyId, int[] tags)
        {
            return Put<int[],Story>(String.Format("projects/{0}/stories/{1}/tags", projectId, storyId), tags);
        
        }

        /// <summary>
        /// Gets the story.
        /// </summary>
        /// <param name="storyId">The story id.</param>
        /// <param name="projectId">The project id.</param>
        /// <returns></returns>
        public Story GetStory(int projectId, int storyId )
        {
            return GetStory(projectId, storyId, null);
        }

        /// <summary>
        /// Gets the story.
        /// </summary>
        /// <param name="storyId">The story id.</param>
        /// <param name="projectId">The project id.</param>
        /// <param name="enrichments">The enrichments.</param>
        /// <returns></returns>
        public Story GetStory(int projectId, int storyId, params string[] enrichments)
        {
            string param = String.Empty;
            if (enrichments != null)
            {
                param = String.Format("?with={0}", String.Join(",", enrichments));
            }

            return Get<Story>(String.Format("projects/{0}/stories/{1}{2}", projectId,storyId, param));
        }

        public void UpdateStory(int projectId,int storyId, ref Story story)
        {
            story = Put<Story>(String.Format("projects/{0}/stories/{1}", projectId, storyId), story);
        }

        public void DeleteStory(int projectId, int storyId) 
        {
            Delete(String.Format("projects/{0}/stories/{1}", projectId, storyId));
        }

        #endregion

        #region Roles

        public void CreateRole(int projectId, ref Role role)
        {
            role = Post<Role>(String.Format("projects/{0}/roles", projectId), role);
        }

        public Roles GetRoles(int projectId)
        {
            return Get<Roles>(String.Format("projects/{0}/roles", projectId));
        }

        public Role GetRole(int projectId, int roleId)
        {
            return Get<Role>(String.Format("projects/{0}/roles/{1}", projectId, roleId));
        }

        public void UpdateRole(int projectId, ref Role role)
        {
            Put<Role>(String.Format("projects/{0}/roles/{1}", projectId, role.Id), role);
        }

        public void DeleteRole(int projectId, int roleId)
        {
            Delete(String.Format("projects/{0}/roles/{1}", projectId, roleId));
        }

        public People GetPeopleInRole(int projectId, int roleId) 
        {
            return Get<People>(String.Format("projects/{0}/roles/{1}/members", projectId, roleId));
        }

        public People AddPersonToRole(int projectId, int roleId, Person person)
        {
            return Post<Person, People>(String.Format("projects/{0}/roles/{1}/members", projectId, roleId), person);
        }

        public People SetMembersOfRole(int projectId, int roleId, int[] members)
        {
            return Put<int[],People>(String.Format("projects/{0}/roles/{1}/members", projectId, roleId), members);
        }

        public void RemoveMemberFromRole(int projectId, int roleId, int userId)
        {
            Delete(String.Format("projects/{0}/roles/{1}/members/{2}", projectId, roleId, userId));
        }


        #endregion

        #region Tasks

        public void CreateTask(int projectId, int storyId, ref Task task)
        { 
            task = Post<Task>(String.Format("projects/{0}/stories/{1}/tasks", projectId, storyId), task);
        }

        public Task GetTask(int projectId, int storyId, int taskId)
        {
            return Get<Task>(String.Format("projects/{0}/stories/{1}/tasks/{2}", projectId, storyId, taskId));
        }

        public void UpdateTask(int projectId, int storyId, ref Task task)
        {
            Put<Task>(String.Format("projects/{0}/stories/{1}/tasks/{2}", projectId, storyId, task.Id), task);
        }

        public void DeleteTask(int projectId, int storyId, int taskId)
        {
            Delete(String.Format("projects/{0}/stories/{1}/tasks/{2}", projectId, storyId, taskId));
        }

        public Tasks ReorderTasks(int projectId, int storyId, int[] order)
        {
            return Put<int[],Tasks>(String.Format("projects/{0}/stories/{1}/tasks", projectId, storyId), order);
        }

        #endregion

        #region Phases

        public void CreatePhase(int projectId, ref Phase phase)
        {
            phase = Post<Phase>(String.Format("projects/{0}/phases", projectId), phase);
        }

        public Phases GetPhases(int projectId)
        {
            return Get<Phases>(String.Format("projects/{0}/phases", projectId));
        }

        public Phase GetPhase(int projectId, int phaseId)
        {
            return Get<Phase>(String.Format("projects/{0}/phases/{1}", projectId,phaseId));
        
        }

        public void UpdatePhase(int projectId, ref Phase phase) 
        {
            phase = Put<Phase>(String.Format("projects/{0}/phases/{1}", projectId, phase.Id), phase);
        }

        public void DeletePhase(int projectId, int phaseId) 
        {
            Delete(String.Format("projects/{0}/phases/{1}", projectId, phaseId));
        }

        public Phases ReorderPhases(int projectId, int[] order) 
        {
            return Put<int[],Phases>(String.Format("projects/{0}/phases", projectId), order);
        }

        #endregion


        #region Tags

        public void CreateTag(int projectId, ref Tag tag)
        {
            tag = Post<Tag>(String.Format("projects/{0}/tags", projectId), tag);
        }

        public void CreateTag(int projectId, int storyId, ref Tag tag)
        {
            tag = Post<Tag>(String.Format("projects/{0}/stories/{1}/tags", projectId, storyId), tag);
        }

        public Tags GetTags(int projectId)
        {
            return Get<Tags>(String.Format("projects/{0}/tags", projectId));
        }

        public Tag GetTag(int projectId, int tagId)
        {
            return Get<Tag>(String.Format("projects/{0}/tags/{1}", projectId, tagId));
        }

        public Tag GetTag(int projectId, int storyId, int tagId)
        {
            return Get<Tag>(String.Format("projects/{0}/stories/{1}/tags/{2}", projectId, storyId, tagId));
        }

        public void UpdateTag(int projectId, ref Tag tag)
        {
            Put<Tag>(String.Format("projects/{0}/tags/{1}", projectId, tag.Id), tag);
        }

        public void UpdateTag(int projectId, int storyId, ref Tag tag)
        {
            Put<Tag>(String.Format("projects/{0}/stories/{1}/tags/{2}", projectId, storyId, tag.Id), tag);
        }

        public void DeleteTag(int projectId, int tagId)
        {
            Delete(String.Format("projects/{0}/tags/{1}", projectId, tagId));
        }

        public void DeleteTag(int projectId, int storyId, int tagId)
        {
            Delete(String.Format("projects/{0}/stories/{1}/tags/{2}", projectId, storyId, tagId));
        }

        #endregion

        #region Comments

        public Comments GetComments(int projectId, int storyId)
        {
            return Get<Comments>(String.Format("projects/{0}/stories/{1}/comments", projectId, storyId));
        }

        public Comment GetComment(int projectId, int storyId, int commentId)
        {
            return Get<Comment>(String.Format("projects/{0}/stories/{1}/comments/{2}", projectId, storyId, commentId));
        }

        public Comment CreateComment(int projectId, int storyId, ref Comment comment)
        {
            return Post<Comment>(String.Format("projects/{0}/stories/{1}/comments", projectId, storyId), comment);
        }

        public Comment UpdateComment(int projectId, int storyId, ref Comment comment)
        {
            return Put<Comment>(String.Format("projects/{0}/stories/{1}/comments/{2}", projectId, storyId, comment.Id),comment);
        }

        public void DeleteComment(int projectId, int storyId, int commentId)
        {
            Delete(String.Format("projects/{0}/stories/{1}/comments/{2}", projectId, storyId, commentId));
        }

        #endregion

        /// <summary>
        /// Finds the API key.
        /// </summary>
        /// <returns></returns>
        private string FindApiKey()
        {
            return ConfigurationManager.ConnectionStrings["X-Zen-ApiKey"].ConnectionString;
        }

        /// <summary>
        /// Gets the specified objects.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        private T Get<T>(string path) where T : AgileZenModel
        {
            using(var client = new HttpClient(new Uri("https://agilezen.com/api/v1/")))
            {
                client.DefaultRequestHeaders.Add("X-Zen-ApiKey", FindApiKey());
                
                //this is json but could be XML or whatever the API can supply
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                var response = client.Get(path);

                if (response != null)
                {
                    response.EnsureSuccessStatusCode();
                    
                    //this will be XML
                    T result = response.Content.ReadAsJsonDataContract<T>();
                    result.Reset();

                    return result;
                }
            }

            return default(T);
        }

        /// <summary>
        /// Updates the specified objects.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        private T Post<T>(string path, T obj) where T : AgileZenModel
        {
            using (var client = new HttpClient(new Uri("https://agilezen.com/api/v1/")))
            {
                client.DefaultRequestHeaders.Add("X-Zen-ApiKey", FindApiKey());
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                var response = client.Post(path, obj.ToContentUsingDataContractJsonSerializer());

                if (response != null)
                {
                    response.EnsureSuccessStatusCode();

                    T result = response.Content.ReadAsJsonDataContract<T>();
                    result.Reset();

                    return result;
                }
            }

            return default(T);
        }




        /// <summary>
        /// Updates the specified objects.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        private TResponse Post<TRequest, TResponse>(string path, TRequest obj) where TResponse : AgileZenModel
        {
            using (var client = new HttpClient(new Uri("https://agilezen.com/api/v1/")))
            {
                client.DefaultRequestHeaders.Add("X-Zen-ApiKey", FindApiKey());
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                var response = client.Post(path, obj.ToContentUsingDataContractJsonSerializer());

                if (response != null)
                {
                    response.EnsureSuccessStatusCode();

                    TResponse result = response.Content.ReadAsJsonDataContract<TResponse>();
                    result.Reset();

                    return result;
                }
            }

            return default(TResponse);
        }

        /// <summary>
        /// Updates the specified objects.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        private T Put<T>(string path, T obj) where T : AgileZenModel
        {
            using (var client = new HttpClient(new Uri("https://agilezen.com/api/v1/")))
            {
                client.DefaultRequestHeaders.Add("X-Zen-ApiKey", FindApiKey());
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                var response = client.Put(path, obj.ToContentUsingDataContractJsonSerializer());

                if (response != null)
                {
                    response.EnsureSuccessStatusCode();

                    T result = response.Content.ReadAsJsonDataContract<T>();

                    result.Reset();

                    return result;
                }
            }

            return default(T);
        }

        /// <summary>
        /// Updates the specified objects.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        private TResponse Put<TRequest, TResponse>(string path, TRequest obj) where TResponse : AgileZenModel
        {
            using (var client = new HttpClient(new Uri("https://agilezen.com/api/v1/")))
            {
                client.DefaultRequestHeaders.Add("X-Zen-ApiKey", FindApiKey());
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                var response = client.Put(path, obj.ToContentUsingDataContractJsonSerializer());

                if (response != null)
                {
                    response.EnsureSuccessStatusCode();

                    TResponse result = response.Content.ReadAsJsonDataContract<TResponse>();

                    result.Reset();

                    return result;
                }
            }

            return default(TResponse);
        }




        /// <summary>
        /// Deletes the specified objects.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        private void Delete(string path)
        {
            using (var client = new HttpClient(new Uri("https://agilezen.com/api/v1/")))
            {
                client.DefaultRequestHeaders.Add("X-Zen-ApiKey", FindApiKey());

                var response = client.Delete(path);

                if (response != null)
                {
                    response.EnsureSuccessStatusCode();
                }
            }
        }

        /// <summary>
        /// Updates the specified objects.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        private T Delete<T>(string path) where T : AgileZenModel
        {
            using (var client = new HttpClient(new Uri("https://agilezen.com/api/v1/")))
            {
                client.DefaultRequestHeaders.Add("X-Zen-ApiKey", FindApiKey());
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                var response = client.Delete(path);

                if (response != null)
                {
                    response.EnsureSuccessStatusCode();

                    T result = response.Content.ReadAsJsonDataContract<T>();

                    result.Reset();

                    return result;
                }
            }

            return default(T);
        }
    }
}
